<?php
if (! defined('IN_SITE')) {
	exit('invalid request');
}

define('TRUST_UTYPE_TAOBAO', 0);
define('TRUST_UTYPE_WEIBAO', 1);

/**
 * 通行证类（包含注册、登录、退出、修改密码等操作）
 */
class Passport {
	private $tbl_member;
	private $tbl_trust;
	private $errors;

	public function __construct()
	{
		$this->tbl_member = Db::tname('member');
		$this->tbl_trust = Db::tname('trust');
		$this->errors = array(
			'LOGINNAME_INVALID'	=> '手机号输入错误',
			'LOGINNAME_IN_USE'	=> '账号已经被占用',
			'PASSWORD_INVALID'	=> '请输入六位密码',
			'PASSWORD_NOT_SAME'	=> '密码输入不一致',
			'LOGIN_FAILED'		=> '用户名或密码错误',
			'SYSTEM_ERROR'		=> '系统错误',

		);
	}

	/**
	 * 帐号/密码注册。
	 * @param string $loginname
	 * @param string $password
	 * @param string $password2
	 * @param string $realname
	 * @param string $idcard
	 * @return array 注册成功的用户信息数组，或者报错信息。
	 */
	public function register($loginname, $password, $password2, $realname='', $idcard='',$reg_entry,$reg_device)
	{
		DB::beginTransaction();

		try {
			// 登录帐号强制小写
			$loginname = strtolower($loginname);
			$this->checkLoginname($loginname);

			// 判断是否已经为注册用户
			$id = $this->checkLognameuse($loginname);
			if ($id) {
				// 注册用户用户已经被使用
				throw new Exception($this->errors['LOGINNAME_IN_USE']);
			}

			// 向member表插入数据
			$rc = $this->insertMember($loginname, $password, $password2,$reg_entry,$reg_device);
			if ($rc != 1) {
				throw new Exception($this->errors['SYSTEM_ERROR']);
			}
			$uid = DB::lastInsertId();

			// 向user_info表插入数据
			$rc = $this->insertUserinfo($uid, $realname, $loginname, $idcard);
			if ($rc != 1) {
				throw new Exception($this->errors['SYSTEM_ERROR']);
			}

			DB::commit();
			return $this->set_login_status($uid);

		} catch (Exception $e) {

			DB::rollBack();

			$err_type = array_search($e->getMessage(), $this->errors);
			if ($err_type == false) {
				$err_type = 'SYSTEM_ERROR';
			}
			return array(
				'err_type' => $err_type,
				'err_msg' => $e->getMessage()
			);
		}
	}

	/**
	 * 向member表插入数据
	 * @param $loginname
	 * @param $password
	 * @param $password2
	 * @return boolean 返回插入数据的结果
	 */
	function insertMember($loginname, $password, $password2,$reg_entry,$reg_device)
	{
		// 从手机号中截取出 nickname
		$nickname = '用户_'.$GLOBALS['timestamp'];
		$ip = client_ip();
		$salt = random(6);
		$this->checkPassword($password, $password2);
		$password = $this->calcPassword($password, $salt);

		//从cookie中获取用户的来源
		$acc = jgetcookie('acc');
		$landing_url = jgetcookie('landing_url');
		$member = array(
				'loginname'			=> $loginname,
				'nickname'			=> $nickname,
				'password'			=> $password,
				'regip'				=> $ip,
				'regdate'			=> $GLOBALS['timestamp'],
				'lastip'			=> $ip,
				'lastactivity'		=> $GLOBALS['timestamp'],
				'salt'				=> $salt,
				'src'				=> $acc,
				'landing_url'		=> $landing_url,
				'reg_entry'			=> $reg_entry,
				'reg_device'		=> $reg_device,
		);
		$result = DB::insertOrDiscard(DB::tname('member'), $member);
		return $result;
	}

	/**
	 * 向userinfo表插入数据
	 * @param 	$uid
	 * @param	$realname
	 * @param	$loginname
	 * @param	$idcard
	 * @return	boolean 返回插入数据的结果
	 *
	 */
	function insertUserinfo($uid, $realname, $loginname, $idcard)
	{
		//插入userinfo数据
		$userinfo = array(
				'uid' 			=> $uid,
				'realname'		=> $realname,
				'phone'			=> $loginname,
				'phone_verified'=> 1,
				'risk_appetite'	=> 0,
				'gender'		=> 0,
				'birth'			=> 0,
				'idcard'		=> $idcard,
				'qq'			=> '',
				'email'			=> '',
				'email_verified'=> 0,
		);
		$result = DB::insertOrDiscard(DB::tname('user_info'), $userinfo);
		return $result;
	}

	/**
	 * 帐号/密码登录。
	 * @param string $loginname
	 * @param string $password
	 * @param boolean $keeplogin
	 * @return boolean/array 登录成功的用户信息数组，false 表示登录失败。
	 */
	public function login($loginname, $password, $keeplogin)
	{
		// 登录帐号强制小写
		$loginname = strtolower($loginname);
		$loginname = Db::makeSqlValue($loginname);
		$sql = "SELECT * FROM {$this->tbl_member} WHERE loginname={$loginname}";
		$member = Db::querySingleRow($sql);
		if (empty($member))
			return array('err_msg' => $this->errors['LOGIN_FAILED']);

		if ($this->calcPassword($password, $member['salt']) != $member['password'])
			return array('err_msg' => $this->errors['LOGIN_FAILED']);

		$this->set_login_status($member, $keeplogin);
		return $member;
	}

	/**
	 * 注销当前的登录。
	 */
	public function logout()
	{
		session_destroy();
		jsetcookie('auth', '', - 311040000);
		jsetcookie('nick', '', - 311040000);
		jsetcookie('telphone', '', - 311040000);
	}

	/**
	 * 把当前用户的登录信息写入 cookie 保存到客户端浏览器中。
	 * @param array/int $member 用户信息或者 uid。
	 * @param boolean $keeplogin 是否记住登录状态（false 表示关闭浏览器即退出）
	 * @return array 用户信息。
	 */
	public function set_login_status($member, $keeplogin = true)
	{
		if (is_numeric($member)) {
			$sql = "SELECT * FROM {$this->tbl_member} WHERE uid={$member}";
			$member = Db::querySingleRow($sql);
		}

		if (empty($member)) return null;

		// 把登录凭据保存到浏览器 cookie 中
		$life = $keeplogin ? 86400 * 365 * 3 : 0;
		$authcode_time = $keeplogin ? 86400 * 365 * 3 : 36000;
		jsetcookie('auth', authcode("{$member['password']}\t{$member['uid']}", 'ENCODE', '', $authcode_time), $life, true);

		// 把登录用户昵称保存到浏览器 cookie 中（便于静态页面直接显示用户名，而不必查询服务器）
		jsetcookie('nick', $member['nickname'], 86400 * 365 * 3);
		jsetcookie('telphone', $member['loginname'], 86400 * 365 * 3);
		return $member;
	}

	/**
	 * 根据 cookie 把当前用户的登录信息加载到内存中。
	 */
	public function get_login_status()
	{
		// 解析 cookie 中携带的身份信息
		$auth = jgetcookie('auth');
		list($password, $uid) = explode("\t", authcode($auth, 'DECODE'));
		$uid = max(0, (int)$uid);
		if (empty($uid)) return null;

		// 从数据库中读取用户信息
		$password = Db::makeSqlValue($password);
		$sql = "SELECT * FROM {$this->tbl_member} WHERE uid={$uid} AND password={$password}";
		$member = Db::querySingleRow($sql);
		$is_adminoperator = in_array($member['loginname'], $GLOBALS['config']['operators']);

		// 假扮指定用户
		$imp_uid = jgetcookie('imp');
		if ($is_adminoperator && !empty($imp_uid)) {
			$sql = "SELECT * FROM {$this->tbl_member} WHERE uid={$imp_uid}";
			$member = Db::querySingleRow ( $sql );
			$GLOBALS['imp_to'] = $member;
			$GLOBALS['smarty']->assign('imp_to', $member);
		}
		return $member;
	}

	/**
	 * 检查指定的 loginname 是否符合要求可以用来注册新用户。
	 * @param string $loginname
	 * @throws Exception
	 */
	private function checkLoginname($loginname)
	{
		if (strlen($loginname) > 100) {
			throw new Exception($this->errors['LOGINNAME_INVALID']);
		}

		if (!jfilter($loginname, 'mobile')) {
			throw new Exception($this->errors['LOGINNAME_INVALID']);
		}

	}
	/**
	 * 检查用户名是否被使用
	 * @param string 登陆名称
	 * @return $uid 若存在就返回用户id
	 */
	private function checkLognameuse($loginname){
		$loginname = Db::makeSqlValue($loginname);

		$sql = "SELECT uid FROM {$this->tbl_member} WHERE loginname={$loginname}";
		$uid = Db::querySingleValue($sql);
		return $uid;
	}

	/**
	 * 检查指定的 password 是否符合要求可以用来注册新用户。
	 * @param string $password
	 * @param string $password2
	 * @throws Exception
	 */
	private function checkPassword($password, $password2)
	{
		if (strlen($password) < 6) {
			throw new Exception($this->errors['PASSWORD_INVALID']);
		}
		if ($password !== $password2) {
			throw new Exception($this->errors['PASSWORD_NOT_SAME']);
		}
	}

	/**
	 * 用密码明文计算出密码密文。
	 * @param string $password
	 * @param string $salt
	 * @return string
	 */
	public function calcPassword($password, $salt)
	{
		$sfa = md5(md5($password) . $salt);
		return md5(md5($password) . $salt);
	}

	/**
	 * 用信任身份登录。
	 * 如果是新的信任身份，则同时创建一个新的本地账号，并绑定在一起。
	 * @param int $utype
	 * @param string $ucode
	 * @param string  $nickname
	 * @param mixed $credential
	 * @return array 登录成功的用户信息数组，或者报错信息。
	 */
	public function trust_login($utype, $ucode, $nickname, $credential)
	{
		try {
			// 登录验证
			$sql = "SELECT * FROM {$this->tbl_trust} WHERE utype='{$utype}' AND ucode='{$ucode}'";
			$trust = Db::querySingleRow($sql);
			if (!empty($trust['uid'])) {
				$sql = "SELECT * FROM {$this->tbl_member} WHERE uid={$trust['uid']}";
				$member = Db::querySingleRow($sql);
				if (!empty($member)) {
					$this->set_login_status($member);
					return $member;
				}
				// trust 表有记录，但是 member 表没有记录？……
				throw new Exception($this->errors['SYSTEM_ERROR']);
			}

			// 按特定算法生成登录账号/密码
			$loginname = "{$utype}:{$ucode}";
			$password = md5($loginname . $GLOBALS['config']['auth_key']);

			// 注册新用户
			$ip = client_ip();
			$salt = random(6);
			$member = array(
				'loginname'			=> $loginname,
				'nickname'			=> $nickname,
				'password'			=> $this->calcPassword($password, $salt),
				'regip'				=> $ip,
				'regdate'			=> $GLOBALS['timestamp'],
				'lastip'			=> $ip,
				'lastactivity'		=> $GLOBALS['timestamp'],
				'email'				=> '',
				'email_verified'	=> 0,
				'phone'				=> '',
				'phone_verified'	=> 0,
				'salt'				=> $salt,
			);
			$trust = array(
				'utype'				=> $utype,
				'ucode'				=> $ucode,
				'credential'		=> json_encode($credential),
			);

			Db::beginTransaction();
			$rc = Db::insertOrDiscard($this->tbl_member, $member);
			if ($rc != 1) {
				Db::rollBack();
				throw new Exception($this->errors['SYSTEM_ERROR']);
			}
			$uid = Db::lastInsertId();
			if (empty($uid)) {
				Db::rollBack();
				throw new Exception($this->errors['SYSTEM_ERROR']);
			}

			$member['uid'] = $uid;
			$trust['uid'] = $uid;
			$rc = Db::insertOrDiscard($this->tbl_trust, $trust);
			if ($rc != 1) {
				Db::rollBack();
				throw new Exception($this->errors['SYSTEM_ERROR']);
			}
			Db::commit();

			$this->set_login_status($member);
			return $member;
		} catch (Exception $e) {
			return array(
				'err_msg'	=> $e->getMessage()
			);
		}
	}

	/**
	 * 白名单
	 * @param $loginname
	 * @return boolean true 有权限 false 没有权限
	 *
	 */
	function authCon($loginname){
		$mod = get_param('mod');
		$code = get_param('code');
		$auth = '';

		foreach($_GET as $val){
			$auth .= $val;
		}
		if(in_array($loginname, $GLOBALS['config']['consolers'])){
			return true;
		}else{
			//判断一级地址权限
			if(in_array($loginname, $GLOBALS['config']['auth'][$mod])){
				if(array_key_exists($auth, $GLOBALS['config']['auth'])){
					if(in_array($loginname, $GLOBALS['config']['auth'][$auth])){
						return true;
					}else{
						return false;
					}
				}else{
					return true;
				}
			}else{
				return false;
			}
		}

	}
}
